home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_c / cserial / 8250xon.c < prev    next >
C/C++ Source or Header  |  1990-04-04  |  13KB  |  494 lines

  1. /*
  2.  *                               8250XON.C
  3.  *
  4.  *                   NSC8250 RS232 Xon/Xoff ISR Routine
  5.  *
  6.  *                           Written for the
  7.  *
  8.  *                              Datalight
  9.  *                           Microsoft V 5.x
  10.  *                                TurboC
  11.  *                                  &
  12.  *                               Zortech
  13.  *
  14.  *                             C Compilers
  15.  *
  16.  *            Copyright (c) John Birchfield 1987, 1988, 1989
  17.  */
  18.  
  19.  
  20. #include <stdio.h>
  21. #include "dependnt.h"
  22. #include "delay.h"
  23. #include "8250nsc.h"
  24. #include "8250xon.h"
  25. #include "queue.h"
  26. #include "timer.h"
  27.  
  28. #if (!defined (TRUE))
  29. #    define TRUE (1)
  30. #    define FALSE (0)
  31. #endif
  32.  
  33.  
  34. #define I_BUF_SIZE 4096 /* inbyteut  Buffer Size  */
  35. #define O_BUF_SIZE 4096 /* output Buffer Size */
  36.  
  37. volatile unsigned XON_PORT_address = 0x03F8;
  38. volatile char     xoff_enabled     = FALSE,
  39.                   xoff_sent        = FALSE, 
  40.                   xoff_received    = FALSE,
  41.                   XON_PORT_status  = 0,
  42.                   XON_PORT_state   = 0,
  43.                   XON_PORT_command = 0,
  44.                   XON_char         = 0x11,
  45.                   XOFF_char        = 0x13;
  46.  
  47. char          XON_PORT_channel = 1,
  48.               SAVE_int_mask = 0,      /* saved interrupt controller mask word */
  49.  
  50. /*
  51.  *    8250 register save locations and base address offsets
  52.  */
  53.         IER_save = 0, LCR_save = 0, MCR_save = 0, DL_lsb = 0, DL_msb = 0;
  54.  
  55.  
  56.  
  57.  
  58. QUEUE  *xon8250_inqueue, *xon8250_outqueue;
  59.  
  60. /*
  61.  *    XON8250_ISR - This is the interrupt handler for the
  62.  *                    National Semiconducter 8250 Serial Chip.
  63.  *                    After installation by Catch_Rt, it catches the
  64.  *                    8250 interrupts and en_queues incoming characters
  65.  *                    from the Serial Port - and de-queues outgoing
  66.  *                    characters to the Serial Port.  The original code
  67.  *                    was written in assembler and provided about 80%
  68.  *                    of the Port's Bandwidth at 9600 baud running
  69.  *                    An Xmodem protocol.  We'll see what this does...
  70.  */
  71.  
  72. #if (!defined (DLC))
  73. void    (interrupt far * xon_save_vec) (void);
  74. void interrupt far 
  75. xon8250_isr (void)
  76. #else
  77. int 
  78. xon8250_isr ()
  79. #endif
  80. {
  81.     int     ch;
  82.     char    test_status;
  83.  
  84.     enable ();
  85.     test_status = inbyte ((XON_PORT_address + IIR));
  86.     do
  87.     {
  88.         switch (test_status)
  89.         {
  90.             case IIR_rls:
  91.                 XON_PORT_status |= inbyte ((XON_PORT_address + LSR));
  92.                 break;
  93.  
  94.             case IIR_receive:
  95.                 ch = inbyte (XON_PORT_address);
  96.                 if ((xoff_enabled && !xoff_sent) &&
  97.                     (ch == XOFF_char))
  98.                 {
  99.                     xoff_received = TRUE;
  100.                 }
  101.                 else
  102.                     if (xoff_received &&
  103.                         (ch == XON_char))
  104.                 {
  105.                     xoff_received = FALSE;
  106.                     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
  107.                 }
  108.                 else
  109.                     if ((en_queue (xon8250_inqueue, ch) < 10) &&
  110.                         xoff_enabled && !xoff_sent && !xoff_received)
  111.                 {
  112.                     xoff_sent = TRUE;
  113.                     while ((inbyte ((XON_PORT_address + LSR)) & 0x20) == 0)
  114.                         ;
  115.                     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
  116.                     outbyte (XON_PORT_address, XOFF_char);
  117.                 }
  118.                 break;
  119.  
  120.             case IIR_transmit:
  121.                 if (xoff_sent && (queue_avail (xon8250_inqueue) > 20))
  122.                 {
  123.                     xoff_sent = FALSE;
  124.                     outbyte (XON_PORT_address, XON_char);
  125.                 }
  126.                 else
  127.                 if (xoff_received)
  128.                     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
  129.                 else
  130.                 if ((ch = de_queue (xon8250_outqueue)) != -1)
  131.                 {
  132.                     outbyte (XON_PORT_address, ch);
  133.                 }
  134.                 else
  135.                 {
  136.                     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
  137.                 }
  138.                 break;
  139.  
  140.             case IIR_mstatus:
  141.                 test_status = inbyte ((XON_PORT_address + MSR));
  142.                 break;
  143.         }
  144.     } while ((test_status = inbyte (XON_PORT_address + IIR)) != IIR_complete);
  145.     disable ();
  146.     outbyte (INT_cntrl, EOI_word);
  147. #if (defined (DLC))
  148.     return (1);
  149. #endif
  150. }
  151.  
  152.  
  153.  
  154.  
  155. /*
  156.  *    XON8250_INIT - Here we get the address of the 8250 Port
  157.  *                    which corresponds to the channel passed in.
  158.  *                    We then massage the 8259 Interrupt Controller
  159.  *                    calculate the Physical Interrupt and save off
  160.  *                    the 8250's current contents.  Then attach the
  161.  *                    xon8250_isr routine to the interrupt and
  162.  *                    return the rt returned index for saving - it's
  163.  *                    needed to terminate the interrupt.
  164.  */
  165.  
  166. #define XON8250_STACK_SIZE 512
  167. int        xon8250_intno;
  168. static int xon_intmask [] = { 0xef, 0xf7, 0xef, 0xf7 };
  169. /*
  170.  * The above 8259 mask bits are determined from the formula
  171.  *          mask = ~(1 << (5 - PORT_CHANNEL));
  172.  * The array assumes that COM3 and COM4 use the same interrupts
  173.  * as COM1 and COM2.
  174.  */
  175. static int xon_intno   [] = { 12, 11, 12, 11 };
  176. /*
  177.  * The above interrupt number array is based on the algorithm
  178.  *       xon8250_intno = (13 - PORT_channel);
  179.  */
  180.  
  181.  
  182. int 
  183. xon8250_init (int channel, int buffer_size)
  184. {
  185.     int     Dos_address, mask;
  186.     XON_PORT_channel = channel;
  187.     xon8250_inqueue = alloc_queue (buffer_size);
  188.     xon8250_outqueue = alloc_queue (buffer_size);
  189.     Dos_address = (XON_PORT_channel - 1) * 2;
  190.     peekmem (0x40, Dos_address, XON_PORT_address);
  191.     mask = xon_intmask [XON_PORT_channel-1];
  192.     SAVE_int_mask = inbyte (INT_mask);
  193.     mask &= SAVE_int_mask;
  194.     xon8250_intno = xon_intno [XON_PORT_channel-1];
  195.     LCR_save = inbyte (XON_PORT_address + LCR);
  196.     disable ();
  197.     outbyte (XON_PORT_address + LCR, LCR_save | LCR_DLAB);
  198.     MCR_save = inbyte (XON_PORT_address + MCR);
  199.     DL_lsb = inbyte (XON_PORT_address);
  200.     DL_msb = inbyte (XON_PORT_address + 1);
  201.     outbyte (XON_PORT_address + LCR, LCR_save & 0x7F);
  202.     IER_save = inbyte (XON_PORT_address + IER);
  203.     enable ();
  204. #if (defined (DLC))
  205.     int_intercept (xon8250_intno, &xon8250_isr, XON8250_STACK_SIZE);
  206. #else
  207.     xon_save_vec = getvect (xon8250_intno);
  208.     setvect (xon8250_intno, xon8250_isr);
  209. #endif
  210.     DELAY_init ();
  211.     outbyte (INT_mask, mask);
  212. }
  213.  
  214.  
  215.  
  216.  
  217. /*
  218.  *    XON8250_TERM - This routine restores the rs232xon 8250 back to its
  219.  *                    state before xon8250_INIT was called and releases the
  220.  *                    corresponding interrupt back to the system.
  221.  */
  222.  
  223. void 
  224. xon8250_term (int restore)
  225.     disable ();
  226.     outbyte (INT_mask, SAVE_int_mask);
  227.     if (restore)
  228.     {
  229.         outbyte (XON_PORT_address + LCR, LCR_DLAB);
  230.         outbyte (XON_PORT_address, DL_lsb);
  231.         outbyte (XON_PORT_address + 1, DL_msb);
  232.         outbyte (XON_PORT_address + MCR, MCR_save);
  233.         outbyte (XON_PORT_address + LCR, 0x7F);
  234.         outbyte (XON_PORT_address + IER, IER_save);
  235.         outbyte (XON_PORT_address + LCR, LCR_save);
  236.     }
  237. #if (defined (DLC))
  238.     int_restore (xon8250_intno);
  239. #else
  240.     setvect (xon8250_intno, xon_save_vec);
  241. #endif
  242. }
  243.  
  244.  
  245.  
  246.  
  247. /*
  248.  *    XON8250_READ - this routine looks in the xon8250_inqueue for a character
  249.  */
  250.  
  251. int 
  252. xon8250_read (void)
  253. {
  254.     int     ch;
  255.     disable ();
  256.     ch = de_queue (xon8250_inqueue);
  257.     enable ();
  258.     if ((XON_PORT_command == RX_enable) &&
  259.         ((!queue_empty (xon8250_outqueue)) || xoff_sent))
  260.         outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
  261.     return (ch);
  262. }
  263.  
  264.  
  265.  
  266.  
  267. /*
  268.  *    XON8250_TIMED_READ - attempts to read rs232 port - if no char
  269.  *                          available in number of seconds passed
  270.  *                          returns -1
  271.  */
  272.  
  273. int 
  274. xon8250_timed_read (int sec)
  275. {
  276.     int     ch;
  277.  
  278.     timer_set ();
  279.     while ((ch = xon8250_read ()) == -1)
  280.         if ((timer_read () / 18) > sec)
  281.             break;
  282.     return (ch);
  283. }
  284.  
  285.  
  286.  
  287.  
  288. /*
  289.  *    XON8250_WRITE - plain vanilla write to the port - check to see that
  290.  *                     the chip may need a kick in the pants before returning
  291.  */
  292.  
  293. int 
  294. xon8250_write (char ch)
  295. {
  296.     int     rval = -1;
  297.     disable ();
  298.     rval = en_queue (xon8250_outqueue, ch);
  299.     enable ();
  300.     if (XON_PORT_command != RX_TX_enable)
  301.         outbyte (XON_PORT_address + IER, XON_PORT_command = RX_TX_enable);
  302.     return (rval);
  303. }
  304.  
  305.  
  306.  
  307.  
  308. /*
  309.  *    XON8250_DTRNR - drop Data Terminal Ready Line
  310.  */
  311. void
  312. xon8250_dtnr (void)
  313. {
  314.     char mcr_save;
  315.     disable ();
  316.     mcr_save = inbyte (XON_PORT_address + MCR);
  317.     outbyte (XON_PORT_address + MCR, 0);
  318.     DELAY_loop (500);
  319.     outbyte (XON_PORT_address + MCR, mcr_save);
  320.     enable ();
  321. }
  322.  
  323.  
  324.  
  325.  
  326. /*
  327.  *    XON8250_GET_STATUS - returns the current rs232xon status and
  328.  *                          resets any error condition.
  329.  */
  330.  
  331. int 
  332. xon8250_get_status (void)
  333. {
  334.     char    rval = XON_PORT_status;
  335.     XON_PORT_status &= ERROR_reset;
  336.     return ((int) rval);
  337. }
  338.  
  339.  
  340.  
  341.  
  342. /*
  343.  *    XON8250_XOFF_SENT - did we send an Xoff char?
  344.  */
  345.  
  346. int 
  347. xon8250_xoff_sent (void)
  348. {
  349.     return (xoff_sent);
  350. }
  351.  
  352.  
  353.  
  354.  
  355.  
  356. /*
  357.  *    XON8250_WRITE_BUFFER_EMPTY
  358.  */
  359.  
  360. xon8250_write_buffer_empty (void)
  361. {
  362.     return (queue_empty (xon8250_outqueue));
  363. }
  364.  
  365.  
  366.  
  367.  
  368.  
  369. /*
  370.  *    IOCTL_SET_XOFF
  371.  *    sets the rs232 xon/xoff    protocol.  It accepts a command string
  372.  *    of the form
  373.  *                 "y n" or "n n" or "n y" or "y y" either upper or
  374.  *                 lower case.
  375.  *    The 1st char enables or disables xon/xoff receive...
  376.  *    the 2d char enables or disables xon/xoff transmit
  377.  */
  378.  
  379. int 
  380. ioctl_set_xoff (char *cmd)
  381. {
  382.     char    temp;
  383.     char    xrcv[2];
  384.     sscanf (cmd, "%1s", xrcv);
  385.     disable ();
  386.     xoff_enabled = (toupper (*xrcv) == 'Y') ? TRUE : FALSE;
  387.     enable ();
  388. }
  389.  
  390.  
  391.  
  392.  
  393. /*
  394.  *    XON8250_WRITE_BREAK - Write a BREAK Character
  395.  */
  396.  
  397. void 
  398. xon8250_write_break (void)
  399. {
  400.     int     i;
  401.     disable ();
  402.     while ((inbyte (XON_PORT_address + LSR) & 0x40) == 0)
  403.         ;
  404.     outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) | 0x40);
  405.     DELAY_loop (500);
  406.     outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) & 0xBF);
  407.     enable ();
  408. }
  409.  
  410.  
  411.  
  412.  
  413.  
  414. /*
  415.  *    XON8250_XON_PORT_INIT (Cmd) configures the 8250
  416.  *        cmd is a string of the form baud parity stop data xon/xoff... i.e.
  417.  *        300 n 1 8 y
  418.  *
  419.  *        baud - 300, 600, 1200, 2400, 4800, 9600, 19200
  420.  *        parity - n -> no parity check
  421.  *                 o -> odd parity
  422.  *                 e -> even parity
  423.  *        stop   - 1 -> 1 stop bit
  424.  *                 2 -> 2 stop bits
  425.  *        data   - 5, 6, 7, 8 data bits
  426.  */
  427.  
  428. int 
  429. xon8250_port_init (char *cmd)
  430. {
  431.     unsigned baud, data, mode_word, parity, stop, xoff;
  432.     char    pty[2];
  433.     sscanf (cmd, "%d %1s %d %d", &baud, pty, &stop, &data);
  434.     *pty = toupper (*pty);
  435.     switch (*pty)
  436.     {
  437.         case 'E':
  438.             parity = 1;
  439.             break;
  440.         case 'O':
  441.             parity = 3;
  442.             break;
  443.         case 'N':
  444.             parity = 0;
  445.             break;
  446.         default:
  447.             parity = 0;
  448.             break;
  449.     }
  450.     stop = (--stop & 1);
  451.     stop <<= 2;
  452.     baud /= 10;
  453.     baud = 11520 / baud;
  454.     parity <<= 3;
  455.     parity &= 0x018;
  456.     data -= 5;
  457.     data &= 3;
  458.     mode_word = data | stop | parity;
  459.     disable ();
  460.     xoff_received = FALSE;
  461.     outbyte (XON_PORT_address + LCR, inbyte (XON_PORT_address + LCR) | LCR_DLAB);
  462.     outbyte (XON_PORT_address, baud % 256);
  463.     outbyte (XON_PORT_address + 1, baud / 256);
  464.     outbyte (XON_PORT_address + LCR, mode_word & 0x7F);
  465.     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
  466.     outbyte (XON_PORT_address + MCR, 0x0F);
  467.  
  468.     inbyte (XON_PORT_address + LSR);
  469.     inbyte (XON_PORT_address + MSR);
  470.     inbyte (XON_PORT_address);
  471.     enable ();
  472. }
  473.  
  474.  
  475.  
  476.  
  477. /*---------------------- xon8250_port_enable () ----------------------*/
  478. /*
  479.  *
  480.  */
  481. void
  482. xon8250_port_enable (void)
  483. {
  484.     disable ();
  485.     outbyte (XON_PORT_address + IER, XON_PORT_command = RX_enable);
  486.     outbyte (XON_PORT_address + MCR, 0x0F);
  487.  
  488.     inbyte (XON_PORT_address + LSR);
  489.     inbyte (XON_PORT_address + MSR);
  490.     inbyte (XON_PORT_address);
  491.     enable ();
  492. }
  493.